// ==UserScript==
// @copyright	2011-2012 Alesa Dam
// @license	GPL v3+
// @licstart	The following is the entire license notice for this script.
/*
 * Copyright (C) 2009-2011 Alesa Dam
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details 
 * at <http://www.gnu.org/licenses/>.
 */
// @licend
//
// ==/UserScript==
//
// The project page can be found at http://code.google.com/p/flickr-photo-actions-on-thread-pages/
// That is the place to get the latest (beta) version, see known issues listed, or provide 
// feature requests, and patches :-)
//
//

// configuration:

// the Flickr 'small' size is 240 wide, 160 heigh
// the script will reject images that have both dimensions set (or computed) to less than these thresholds:
COMBINED_WIDTH_TRESHOLD=240;
COMBINED_HEIGHT_TRESHOLD=160;

// the script will also reject images that have one of their dimensions set (or computed) below these thresholds:
STRICT_WIDTH_TRESHOLD=30;
STRICT_HEIGHT_TRESHOLD=30;

if (!document.location.href.match(/flickr\.com\/groups\/[^\/]+\/discuss\/\d+/) && // can not use document.location.href.test in Firefox
	!document.location.href.match(/flickr\.com\/photos\/[^\/]+\/\d+/) &&
	!document.location.href.match(/flickr\.com\/groups\/[^\/]+\/admin\/pending/) &&
	!document.location.href.match(/flickr\.com\/groups\/[^\/]+\/pool/) &&
	!document.location.href.match(/flickr\.com\/groups\/[^\/]+\/?$/)) {
	throw new Error("not on a supported page; aborting");
}

// Greased MooTools:
// imported via manifest
// end Greased MooTools

// hack to circumvent 'bug' when overriding toString (and others):
// https://mootools.lighthouseapp.com/projects/2706/tickets/651-classtostring-broken-on-122-big-regression
['toString', 'toLocaleString', 'valueOf', 'toSource', 'watch', 'unwatch', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable'].each(function (method) {
	Class.Mutators[method] = $arguments(0);
});

var images = {
	updating: 'http://www.flickr.com/images/pulser2.gif',
	error:    'http://l.yimg.com/g/images/icon_error_x_small.png',
	check:    'http://l.yimg.com/g/images/icon_check_small.png',
	check_big:    'http://l.yimg.com/g/images/icon_check.png',
	editadd:  chrome.extension.getURL('images/editadd.png'),
	editpaste: chrome.extension.getURL('images/editpaste.png'),
	editdelete: chrome.extension.getURL('images/editdelete.png'),
	edit: chrome.extension.getURL('images/edit.png'),
	filesave: chrome.extension.getURL('images/filesave.png'),
	db: chrome.extension.getURL('images/db.png'),
	paypal: chrome.extension.getURL('images/paypal.png'),
	amazon: chrome.extension.getURL('images/amazon.png'),
	bitcoin: chrome.extension.getURL('images/bitcoin.png'),
	fasttag: 'http://l.yimg.com/g/images/icon_tag.gif'
};

// get version asynchronously
var backgroundPort = chrome.extension.connect({ name: "flickrphotoactionsonthreadpages" });
backgroundPort.onMessage.addListener(function(msg) {
	if (msg.stat == 'error') {
		GM_log("ERROR: " + msg.statusText);
	}
	$(msg.callbackId).fireEvent(msg.callbackEvent, msg);
});

function getVersion(callbackId, callbackEvent) {
	try {
		backgroundPort.postMessage({ name: 'getVersion', callbackEvent: callbackEvent, callbackId: callbackId });
	} catch(e) {
		GM_log("ERROR connecting to the background page: " + e);
	}
}
// end get version asynchronously

// greasemonkey functions
// moved to scripts/greasemonkey_layer.js
// end greasemonkey functions

var threadPage  = document.location.href.match(/flickr\.com\/groups\/[^\/]+\/discuss\/\d+/);
var pendingPage = document.location.href.match(/flickr\.com\/groups\/[^\/]+\/admin\/pending/);
var poolPage	= document.location.href.match(/flickr\.com\/groups\/[^\/]+\/pool/) || document.location.href.match(/flickr\.com\/groups\/[^\/]+\/?$/);
var photoPage   = !threadPage && !pendingPage && !poolPage;

if (document.location.href.match(/view=ju/)) { // skip justified view
	throw new Error("not supported on justivied view pages; aborting");
}
if ($$('body')[0].hasClass('breakout-justified')) { // skip justified view
	throw new Error("not supported on justivied view pages; aborting");
}

// upgrade stored data
var add_referrer = GM_getValue("add_referrer");
if (add_referrer == undefined) {
	GM_log("upgrade: set 'add_referrer' to 'none' (was not defined)");
	GM_setValue("add_referrer", "none");
} else if (add_referrer == 'false' || add_referrer == false) {
	GM_log("upgrade: set 'add_referrer' to 'none' (was set to false)");
	GM_setValue("add_referrer", "none");
} else if (add_referrer == true || add_referrer == 'true') {
	if ($chk(GM_getValue("referrer"))) {
		GM_log("upgrade: set 'add_referrer' to 'custom'");
		GM_setValue("add_referrer", "custom");
	} else {
		GM_log("upgrade: set 'add_referrer' to 'default'");
		GM_setValue("add_referrer", "default");
	}
} // else: already upgraded

function searchGroupName() {
	var groupNameElement = $('main').getElement('span.group-title-holder').clone(true);
	groupNameElement.getElements('*').dispose();
	return groupNameElement.get('text');
}

function searchThreadTitle() {
	var threadTitleElement = $('main').getElement('div.group-topic-detail-col h2').clone(true);
	threadTitleElement.getElements('*').dispose();
	return threadTitleElement.get('text');
}

// this extension first had support for 'stored comments',
// which still reflects in the storage of 'stored tags':
// . fastcomment, would mean 'fasttag' for tags
// . comment, would mean 'tag' for tags

function createFastItemIcon(key, photoId, injectionPoint, type) {
	try {
		var lastLeft = parseInt($('FPAOTP_bar_' + photoId).getElements('span.FPAOTPCommentIcon, span.FPAOTPTagIcon').pop().getStyle('left').match(/(\d+)/)[1], 10); // getLast does not work?
		var lastWidth = parseInt($('FPAOTP_bar_' + photoId).getElements('span.FPAOTPCommentIcon,span.FPAOTPTagIcon').pop().getStyle('width').match(/(\d+)/)[1], 10);
	} catch (e) {
		lastLeft = 80;
		lastWidth = 0;
	}
	if (isNaN(lastLeft)) { lastLeft = 80; }

	var storedItem = GM_getObject(key);
	new Element('span', {
		title: 'Fast ' + (type == 'comments' ? 'comment': 'tag') + ': ' + storedItem.title,
		id: 'FPAOTP_fastitem_' + key + '_' + photoId,
		'class': type == 'comments' ? 'FPAOTPCommentIcon' : 'FPAOTPTagIcon',
		html: storedItem.shortlabel,
		styles: {
			backgroundColor: storedItem.bgcolor,
			left: poolPage ? '1px' : (lastLeft + lastWidth) + 'px',
			width: ($chk(storedItem.width) ? storedItem.width : 12) + 'px',
			'text-align': 'center',
			'line-height': '11px',
			'vertical-align': 'top'
		},
		events: {
			click: function (evt) {
				var ids = this.get('id').match(/FPAOTP_fastitem_([^_]+)_(.*)$/);
				var key = ids[1];
				var photoId = ids[2];
				var storedItem = GM_getObject(key);
				var content = storedItem.comment;
				if (threadPage && type == 'Comment' && (GM_getValue("add_referrer") == 'default' || GM_getValue("add_referrer") == 'custom')) {
					var referrer = GM_getValue("add_referrer") == 'default' ? defaultReferrer : $chk(GM_getValue("referrer")) ? GM_getValue("referrer") : defaultReferrer; // a user could have chosen for custom, but never customized
					var threadUrl = document.location.href;
					var threadTitle = searchThreadTitle();
					var groupName = searchGroupName();
					var groupUrl = threadUrl.match(/(.*flickr\.com\/groups\/[^\/]+\/)/)[1];
					referrer = referrer.replace('%__thread_url__%', threadUrl)
							.replace('%__thread_title__%', threadTitle)
							.replace('%__group_url__%', groupUrl)
							.replace('%__group_name__%', groupName)
							.replace(/\n/g, '<br>');
					content = content + '<br>' + referrer;
				}
				if ($chk(content) && content.length > 0) {
					var itemIcon = $('FPAOTP_fastitem_' + key + '_' + photoId);
					itemIcon.empty();
					new Element('img', { src: images.updating, title: 'posting data' }).inject(itemIcon);
					var type = key.match(/stored(.*)\.\d+/)[1];
					if (type == 'Comment') {
					    postComment(photoId, key, content.replace(/<br[^>]*>/g, '\n'), function (retval) {
						var photoId = retval.photoId;
						var key = retval.key;
						var success = retval.success;
						var msg = retval.msg;
						var itemIcon = $('FPAOTP_fastitem_' + key + '_' + photoId);
						itemIcon.empty();
						new Element('img', {
							src: success ? images.check : images.error,
							title: success ? 'comment posted' : msg
						}).inject(itemIcon);
					    });
					} else { //'Tag'
					    postTags(photoId, key, content.replace(/(?:<br[^>]*>|\n)/g, ' '), function (retval) {
						var photoId = retval.photoId;
						var key = retval.key;
						var success = retval.success;
						var msg = retval.msg;
						var itemIcon = $('FPAOTP_fastitem_' + key + '_' + photoId);
						itemIcon.empty();
						new Element('img', {
							src: success ? images.check : images.error,
							title: success ? 'tag posted' : msg
						}).inject(itemIcon);
					    });
					}
				} else {
					new Element('img', {
						src: images.error,
						title: 'nothing to post!'
					}).inject(itemIcon);
				}
			}
		}
	}).inject(injectionPoint);
}

// cache storedComments and storedTags
var storedCommentsCache;
var storedTagsCache;

function reFillCommentsCache() {
	storedCommentsCache = GM_listValuesWithMatch(/^storedComment\./).sort(function(a,b) { var aTitle = GM_getObject(a).title.toLowerCase(); var bTitle = GM_getObject(b).title.toLowerCase(); if (aTitle < bTitle) return -1; if (aTitle > bTitle) return 1; return 0; });
}
function reFillTagsCache() {
	storedTagsCache = GM_listValuesWithMatch(/^storedTag\./).sort(function(a,b) { var aTitle =  GM_getObject(a).title.toLowerCase(); var bTitle = GM_getObject(b).title.toLowerCase(); if (aTitle < bTitle) return -1; if (aTitle > bTitle) return 1; return 0; });
}

reFillCommentsCache();
reFillTagsCache();

function showButtons() {
    $$('img.notsowide[src*=static.flickr.com], img.notsowide[src*=staticflickr.com], div.photo-div img[id*=liquid-photo-buffer], div.gPend * img.pc_img, div.thumb * img.pc_img, span.photo_container * img[src*=flickr.com]').each( function (img) {
	try {
		var width = parseInt(img.get('width').match(/(\d+)/)[1],10);
	} catch (e) {
		try {
			var computedWidth = img.getComputedStyle('width');
			width = parseInt(computedWidth.match(/(\d+)/)[1],10);
		} catch (e) {
			width = 0;
		}
	}
	if (isNaN(width)) width = 0;

	try {
		var height = parseInt(img.get('height').match(/(\d+)/)[1],10);
	} catch (e) {
		try {
			var computedHeight = img.getComputedStyle('height');
			height = parseInt(computedHeight.match(/(\d+)/)[1],10);
		} catch (e) {
			height = 0;
		}
	}
	if (isNaN(height)) height = 0;

	if (width > 0 && width < COMBINED_WIDTH_TRESHOLD &&
	    height > 0 && height < COMBINED_HEIGHT_TRESHOLD) { 
		return;
	}

	if (width > 0 && width < STRICT_WIDTH_TRESHOLD) {
		return;
	}
	if (height > 0 && height < STRICT_HEIGHT_TRESHOLD) { 
		return;
	}
	
	var srcMatch = img.get('src').match(/flickr\.com\/\d+\/(\d+).*jpg/);
	if (!srcMatch) {
		//alert("could not determine Flickr photo id from image 'src' attribute: " + img.get('src'));
		return;
	}
	var photoId = srcMatch[1];
	if (photoPage) {
                var urlPhotoId = document.location.href.match(/flickr\.com\/photos\/[^\/]+\/(\d+)\//)[1];
                if (urlPhotoId != photoId) return; // only do the photo itself (for now)
        }
	if ($chk($('FPAOTP_bar_' + photoId))) return; // already there

	if (threadPage) {
	    var injectionPoint = img.getParent('a') ? img.getParent('a') : img;
	    var injectionDirection = 'before';
	} else if (photoPage) {
	    injectionPoint = new Element('div', {
	    	html: ' ', // some magic
	    	styles: {
			position: 'relative',
			display: 'block'
		}
	    }).inject($('comment-form'), 'before');
	} else if (pendingPage) {
		injectionPoint = img.getParent('td.gPendPic');
		injectionDirection = 'top';
	} else { // poolPage
		injectionPoint = img.getParent('div.hover-target');
		injectionDirection = 'bottom';
	}

	var buttonBar = new Element('div', {
		id: 'FPAOTP_bar_' + photoId,
		styles: {
			display: threadPage || pendingPage || poolPage ? 'block' : 'inline',
			position: 'relative',
			//width: '100px',
			height: '25px'
		},
		events: {
			'item-added': function (evt) {
				var key = evt;
				var storedItem = GM_getObject(key);
					// called twice??
				if (storedItem.fastcomment == true || storedItem.fastcomment == 'true') {
					createFastItemIcon(key, photoId, this, $('FPAOTP_management_dialog').get('FPAOTP-type'));
				}
			},
			'item-updated': function (evt) {
				var key = evt;
				var storedItem = GM_getObject(key);
				var fastitemButton = $('FPAOTP_fastitem_' + key + '_' + photoId);
				if ($chk(fastitemButton)) {
					if (storedItem.fastcomment != true && storedItem.fastcomment != 'true') {
						this.fireEvent('item-removed', key);
					} else {
						try {
							var width = parseInt(fastitemButton.getStyle('width').match(/(\d+)/)[1], 10);
						} catch (e) {
							width = 40 + 1;
						}
						if (storedItem.width <= width) {
							// update label, title, bg-color, width
							fastitemButton.set('title', ($('FPAOTP_management_dialog').get('FPAOTP-type') == 'comments' ? 'Fast comment: ' : 'Fast tag: ') +
								storedItem.title);
							fastitemButton.set('html', storedItem.shortlabel);
							fastitemButton.setStyle('background-color', storedItem.bgcolor);
							fastitemButton.setStyle('width', storedItem.width);
						} else { // add it to the end
							fastitemButton.destroy();
							this.fireEvent('item-added', key);
						}
					}
				} else { // not available
					if (storedItem.fastcomment == true || storedItem.fastcomment == 'true') {
						this.fireEvent('item-added', key);
					}
				}
			},
			'item-removed': function (evt) {
				var key = evt;
				try {
					$('FPAOTP_fastitem_' + key + '_' + photoId).destroy();
				} catch (e) {
				}
			}
		}
	}).inject(injectionPoint, injectionDirection);

	if (threadPage || pendingPage) {
	    new Element('span', {
		id: 'FPAOTP_fav_' + photoId,
		'class': 'FPAOTP_unknown',
		styles: {
			cursor: 'pointer',
			position: 'absolute',
			left: '0px',
			background: 'url(http://l.yimg.com/g/images/photo-sprite.png.v8) no-repeat 0 0',
			'background-position': '-1053px -11px',
			width: '16px',
			height: '16px'
		},
		events: {
			'click': function (evt) {
				var span = $(evt.target);
				if (span.get('class').match('_unknown')) { return; }
				var photoId = span.get('id').replace('FPAOTP_fav_', '');
				if (span.get('class').match('_faved')) {
					unFavorite(photoId);
				} else {
					favorite(photoId);
				}
			}
		}
	    }).inject(buttonBar);
	}
	if (!poolPage) { // no tag or comment dialog on pool pages, only fast tags/comments
	    var tagSpan = new Element('span', {
		title: 'Add a tag',
		id: 'FPAOTP_tag_' + photoId,
		styles: {
			cursor: 'pointer',
			position: 'absolute',
			left: '22px',
			width: '16px',
			height: '16px',
			background: 'url(http://l.yimg.com/g/images/photo-sprite.png.v8) no-repeat -1812px -11px'
		},
		events: {
			'click': function (evt) {
				var span = $(this);
				var photoId = span.get('id').replace('FPAOTP_tag_', '');
				toggleTagDialog(photoId);
			}
		}
	    }).inject(buttonBar);
	    var commentSpan = new Element('span', {
		title: 'Add comment',
		id: 'FPAOTP_comment_' + photoId,
		styles: { 
			cursor: 'pointer',
			position: 'absolute',
			left: '44px',
			width: '16px',
			height: '16px',
			background: 'url(http://l.yimg.com/g/images/photo-sprite.png.v8) no-repeat 0 0',
			'background-position': '-1732px -51px'
		},
		events: {
			'click': function (evt) {
				var span = $(this);
				var photoId = span.get('id').replace('FPAOTP_comment_', '');
				toggleCommentDialog(photoId);
			}
		}
	    }).inject(buttonBar);
	}

	if (photoPage) {
	    new Element('i', {
		html: '&emsp;',
		styles: {
			paddingLeft: '12px',
			paddingRight: '3px'
		}
	    }).inject(commentSpan, 'before');
	}
	
	try {
		var left = parseInt(commentSpan.getComputedStyle('left').match(/(\d+)/)[1], 10);
		if (left < 10) left = undefined;
		else left = left + 16 + 20; // 20: margin; 16: image width
	} catch (e) {
	}
	if (isNaN(left)) { left = 80; }
	// show fast comments and tags
	storedCommentsCache.forEach( function (key, idx) {
		var storedComment = GM_getObject(key);
		if (storedComment.fastcomment == true || storedComment == 'true') {
			createFastItemIcon(key, photoId, buttonBar, 'comments');
		}
	});
	storedTagsCache.forEach( function (key, idx) {
		var storedComment = GM_getObject(key);
		if (storedComment.fastcomment == true || storedComment == 'true') {
			createFastItemIcon(key, photoId, buttonBar, 'tags');
		}
	});
	if (threadPage || pendingPage) {
		checkIfFaved(photoId);
		checkIfCommented(photoId);
	}

    });

    // using DOMNodeInserted event handler could be used
/*    if (photoPage && $$('span[id*=FPAOTP_comment]').length == 0 && count < 10) {
	//GM_log("DEBUG: count=" + count);
	window.setTimeout( function () { showButtons(count+1); }, 1000);
    }
*/
}

if (photoPage) {
	$$('body')[0].addEventListener('DOMNodeInserted', function(event) {
	    setTimeout(function() { // workaround: addEventListener callback gets called in unsafeWindow!
                try {  
                        var target = event.target;
                        if (target.nodeName == 'DIV' && (target.className.match('pool-photo') || target.className.match('fave') || target.className.match('photo-display-item'))) {  
                                showButtons();
                        }
                } catch (e) {
                        GM_log("error on insert: " + e);
                }
            }, 0);
	}, true);
}
if (poolPage) {
	var photos = $$('span.photo_container * img[src*=flickr.com]').length;
	$$('body')[0].addEventListener('DOMSubtreeModified', function(event) {
	    setTimeout(function () {
		try {
			var rPhotos = $$('span.photo_container * img[src*=flickr.com]').length;
			if (rPhotos > photos) {
				photos = rPhotos;
				showButtons();
			}
		} catch (e) {
			GM_log("error insert: " + e);
		}
	    }, 0);
	}, true);
}

//var count = 0;
showButtons();

    function toggleTagDialog(photoId) {
	var tagDialog = $('FPAOTP_tagdialog_' + photoId);
	if ($chk(tagDialog)) {
		tagDialog.destroy();
		return;
	}

	tagDialog = new Element('div', {
            id: 'FPAOTP_tagdialog_' + photoId,
            styles: {
                overflow: 'auto',
                background: '#BFBFBF',
                '-moz-border-radius': '4px',
                '-webkit-border-radius': '4px',
                '-khtml-border-radius': '4px',
                'border-radius': '4px',
                border: 'grey solid 1px',
                padding: '2px 4px',
                display: 'block',
                visibility: 'visible',
		width: '500px',
		'margin-bottom': '5px',
		'text-align': 'left'
            }
        }).inject('FPAOTP_bar_' + photoId, 'after');
	tagDialog.adopt(
	    new Element('label', { html: 'Enter your tag(s): ' }),
	    new Element('select', { 
	    	id: 'FPAOTP_stored_tags_' + photoId,
		events: {
			change: function (evt) {
			    var combo = $(evt.target);
			    var photoId = combo.get('id').replace('FPAOTP_stored_tags_', '');
			    var selected = combo.getSelected();
			    if (selected.get('value') == -1) {
				$('FPAOTP_tagsinput_' + photoId).empty();
			    } else {
				var key = selected.get('value');
				$('FPAOTP_tagsinput_' + photoId).set('value', GM_getObject(key).comment.replace(/\n/g, ' '));
			    }
			},
			'tag-added': function (evt) {
				new Element('option', {
					value: evt,
					html: GM_getObject(evt).title
				}).inject(this);
			},
			'tag-updated': function (evt) {
				this.getElements('option').some(function(option) {
					if (option.get('value') == evt) {
						option.set('html', GM_getObject(evt).title);
						return true; // stop as soon as possible
					}
					return false;
				});
			},
			'tag-removed': function (evt) {
				this.getElements('option').some(function(option) {
					if (option.get('value') == evt) {
						option.dispose();
						return true; // stop as soon as possible
					}
					return false;
				});
			}
		}
	    }),
	    new Element('img', {
		src: images.db,
		title: 'manage your stored tags',
		styles: {
			cursor: 'pointer'
		},
		events: {
			click: function (evt) {
			    toggleManagementDialog('tags');
			}
		}
	    }),
	    new Element('br'),
	    new Element('input', {
		id: 'FPAOTP_tagsinput_' + photoId,
		styles: {
			width: '300px'
		},
	    }),
	    new Element('button', {
		'class': 'Butt',
		html: 'Add tag(s)',
		id: 'FPAOTP_tagbutton_' + photoId,
		styles: {
			margin: '2px'
		},
		events: {
			click: function (evt) {
				var photoId = evt.target.id.replace('FPAOTP_tagbutton_', '');
				var tags = $('FPAOTP_tagsinput_' + photoId).get('value');
				$('FPAOTP_tagdialog_' + photoId).destroy();
				if ($chk(tags) && tags.length > 0) {
					var tagIcon = $('FPAOTP_tag_' + photoId);
					tagIcon.empty();
					new Element('img', { src: images.updating, title: 'posting tags' }).inject(tagIcon);
					postTags(photoId, null, tags, function (retval) {
						var photoId = retval.photoId;
						var success = retval.success;
						var msg = retval.msg;
						var tagIcon = $('FPAOTP_tag_' + photoId);
						tagIcon.empty();
						new Element('img', {
							src: success ? images.check : images.error,
							title: success ? 'tags posted' : msg
						}).inject(tagIcon);
					});
				}
			}
		}
	    }),
	    new Element('button', {
		'class': 'DisabledButt',
		'html': 'Cancel',
		id: 'FPAOTP_cancel_tag_' + photoId,
		styles: {
			margin: '2px'
		},
		events: {
			click: function(evt) {
				var button = $(evt.target);
				var photoId = button.get('id').replace('FPAOTP_cancel_tag_', '');
				toggleTagDialog(photoId);
			}
		},
		styles: {
			cursor: 'pointer'
		}
	    }),
	    new Element('br'),
	    new Element('small', {
		html: 'Separate each tag with a space: <i>cameraphone urban moblog</i>. Or to join 2 words together in one tag, use double quotes: <i>"daily commute"</i>.'
	    })
	);
	// fill the combo
	new Element('option', {
		value: -1,
		html: '--select a stored tag--'
	}).inject($('FPAOTP_stored_tags_' + photoId));
	storedTagsCache.forEach( function (key, idx) {
			new Element('option', {
				value: key,
				html: GM_getObject(key).title
			}).inject($('FPAOTP_stored_tags_' + photoId));
		});
	
	checkIfAllowedToTag(photoId, function(retval) {
		var photoId = retval.photoId;
                var tagIcon = $('FPAOTP_tag_' + photoId);
		tagIcon.empty();
		if (!retval.success) {
			    new Element('img', {
				src: images.error,
				title: retval.msg
			    }).inject(tagIcon);
		}
        });
	  
    }

    function checkIfAllowedToTag(photoId, callback) {
        var tagIcon = $('FPAOTP_tag_' + photoId);
	tagIcon.empty();
	new Element('img', { src: images.updating }).inject(tagIcon);

    	var retval = false;
	var apiData = {
            api_key: GM_getSessionKey(),
            auth_hash: GM_getAuthHash(),
            auth_token: GM_getAuthToken(),
            format: 'json',
            method: 'flickr.photos.getInfo',
	    photo_id: photoId,
            nojsoncallback: 1
        };
	new Request({
		url: 'http://www.flickr.com/',
		onFailure: function (response) {
			callback({ photoId: photoId, success: false, msg: response.statusText });
		},
		onSuccess: function (responseText, responseXml) {
			var result;
                        try {
                            result = JSON.parse(responseText);
                        } catch (e) {
                            result = eval('(' + responseText + ')');
                        }
                        if (result.stat === 'fail') {
				callback({ photoId: photoId, success: false, msg: result.code + " - " + result.message });
                            return;
                        }
			var cantag = result.photo.editability.canaddmeta;
			if (cantag == 1) {
				callback({ photoId: photoId, success: true });
			} else {
				callback({ photoId: photoId, success: false, msg: "no permission to add tags" });
			}
		}
	}).get("/services/rest/", apiData);
    }

    function postTags(photoId, key, tags, callback) {
	var apiData = {
                    api_key: GM_getSessionKey(),
                    auth_hash: GM_getAuthHash(),
                    auth_token: GM_getAuthToken(),
                    format: 'json',
                    nojsoncallback: 1,
                    method: 'flickr.photos.addTags',
                    photo_id: photoId,
                    tags: tags
                };
	new Request({
                    url: "http://www.flickr.com/",
                    onFailure: function (response) {
                        callback({ photoId: photoId, key: key, success: false, msg: response.statusText });
                    },
                    onSuccess: function (responseText, responseXML) {
                        var result;
                        try {
                            result = JSON.parse(responseText);
                        } catch (e) {
                            result = eval('(' + responseText + ')');
                        }  
                        if (result.stat === 'fail') {
                            callback({ photoId: photoId, key: key, success: false, msg: result.code + " - " + result.message });
                        } else {
                            callback({ photoId: photoId, key: key, success: true });
                        }
                    }
                }).get("/services/rest", apiData);
    }

// DEFAULT REFERRER
    var defaultReferrer = '--<br><i>Seen in discussion <a href="%__thread_url__%">%__thread_title__%</a> of the group <a href="%__group_url__%">%__group_name__%</a></i>';

GM_addStyle('img.FPAOTPDisabled { opacity: 0.5; ! important } ');
GM_addStyle('img.FPAOTPEnabled { opacity: 1.0; cursor: pointer ! important } ');
if (poolPage) {
	GM_addStyle('span.FPAOTPCommentIcon { display: inline-block; word-wrap: break-word; cursor: pointer; position: relative; max-width: 24px; height: 10px; background: url(http://l.yimg.com/g/images/photo-sprite.png.v8) no-repeat 0 0; background-position: -55px -15px; } ');
	GM_addStyle('span.FPAOTPTagIcon { display: inline-block; word-wrap: break-word; cursor: pointer; position: relative; max-width: 24px; height: 12px; background: url(http://l.yimg.com/g/images/icon_tag.gif) no-repeat 0 0; background-position: -3px 0px; background-size: contain; } ');
} else {
	GM_addStyle('span.FPAOTPCommentIcon { word-wrap: break-word; cursor: pointer; position: absolute; left: 44px; width: 24px; height: 10px; background: url(http://l.yimg.com/g/images/photo-sprite.png.v8) no-repeat 0 0; background-position: -55px -15px; } ');
	GM_addStyle('span.FPAOTPTagIcon { word-wrap: break-word; cursor: pointer; position: absolute; left: 44px; width: 24px; height: 12px; background: url(http://l.yimg.com/g/images/icon_tag.gif) no-repeat 0 0; background-position: -3px 0px; background-size: contain; } ');
}

    function toggleManagementDialog(type) {
    	var managementDialog = $('FPAOTP_management_dialog');
	if ($chk(managementDialog) && managementDialog.get('FPAOTP-type') == type) {
		return;
	}
	if ($chk(managementDialog)) {
		managementDialog.fireEvent('switch-type', type);
	} else {
	    var topline = 30;
	    managementDialog = new Element('div', {
        	id: 'FPAOTP_management_dialog',
		'FPAOTP-type': type,
        	styles: {
			overflow: 'auto',
			maxHeight: (window.innerHeight - topline - 20),
                        maxWidth: Math.min((window.innerWidth - 50),800),
			minWidth: 640,
			width: (window.innerWidth - 50),
            		padding: '2px 4px',
            		background: '#bfbfbf none repeat scroll 0% 0%',
            		display: 'block',
                	'-moz-border-radius': '4px',
                	'-webkit-border-radius': '4px',
                	'-khtml-border-radius': '4px',
                	'border-radius': '4px',
                	border: 'grey solid 1px',
            		'-moz-background-clip': 'border',
            		'-moz-background-origin': 'padding',
            		'-moz-background-inline-policy': 'continuous',
            		position: 'fixed',
            		opacity: '0.98',
           		'z-index': 1014,
            		top: '5px',
            		left: 25
        	},
		events: {
			'switch-type': function (evt) {
				this.set('FPAOTP-type', evt);
				this.getElements('*[id^=FPAOTP]').fireEvent('switch-type', evt);
			},
			'create-new-item': function (evt) {
				this.getElements('*[id^=FPAOTP]').fireEvent('create-new-item', evt);
			},
			'item-added': function (evt) {
				this.getElements('*[id^=FPAOTP]').fireEvent('item-added', evt);
				$$('select[id^=FPAOTP_stored_comments]').fireEvent('item-added', evt);
				$$('div[id^=FPAOTP_bar_]').fireEvent('item-added', evt);
			},
			'item-updated': function (evt) {
				this.getElements('*[id^=FPAOTP]').fireEvent('item-updated', evt);
				$$('select[id^=FPAOTP_stored_comments]').fireEvent('item-updated', evt);
				$$('div[id^=FPAOTP_bar_]').fireEvent('item-updated', evt);
			},
			'item-copy': function (evt) {
				this.getElements('*[id^=FPAOTP]').fireEvent('item-copy', evt);
			},
			'item-edit': function (evt) {
				this.getElements('*[id^=FPAOTP]').fireEvent('item-edit', evt);
			},
			'item-removed': function (evt) {
				this.getElements('*[id^=FPAOTP]').fireEvent('item-removed', evt);
				$$('select[id^=FPAOTP_stored_comments]').fireEvent('item-removed', evt);
				$$('div[id^=FPAOTP_bar_]').fireEvent('item-removed', evt);
			},
			'update-selection': function (evt) {
				this.getElements('*[id^=FPAOTP]').fireEvent('update-selection', evt);
			}
		}
    	    }).inject($(document).getElement("body"));
	    managementDialog.adopt(
	    	new Element('div').adopt(
	    	    new Element('span', {
			title: 'consider a donation',
			html: 'donate:',
			styles: {
				'float': 'left',
				'font-style': 'italic',
				'font-size': 'smaller',
				'vertical-align': 'middle'
			}
		    }).adopt(
			new Element('a', {
				href: 'https://www.paypal.com/be/cgi-bin/webscr?cmd=_send-money&email=alesadam@ymail.com&amount_ccode=USD&payment_type=S',
				title: 'donate with Paypal',
				target: '_blank'
			}).adopt(
				new Element('img', { src: images.paypal, styles: { 'vertical-align': 'middle' } })
			),
			new Element('a', {
				href: 'http://www.amazon.co.uk/registry/wishlist/RWKOXV8NS09I/',
				title: 'buy me something from my Amazon wishlist (UK)',
				target: '_blank'
			}).adopt(
				new Element('img', { src: images.amazon, styles: { 'vertical-align': 'middle' } })
			),
			new Element('a', {
				href: 'http://www.amazon.com/gp/wishlist/2FWKY0A7NVSOJ/',
				title: 'buy me something from my Amazon wishlist (US)',
				target: '_blank'
			}).adopt(
				new Element('img', { src: images.amazon, styles: { 'vertical-align': 'middle' } })
			),
			new Element('a', {
				href: 'javascript:void(O);',
				title: 'donate Bitcoins',
				events: {
					click: function (evt) {
						// based on http://leo.bogert.de/2012/02/22/simple-bitcoin-donate-button/
						window.prompt ('Please copy-paste my Bitcoin address to your Bitcoin software - I cannot do it automatically.\nTo copy it, right-click the selected text and select \'Copy\'.\nThen right-click the address field in your Bitcoin software and select \'Paste\'.', '19DvtjDgiSx9991xVkXSYSovsSoreSGDC7');
					}
				}
			}).adopt(
				new Element('img', { src: images.bitcoin, styles: { 'vertical-align': 'middle' } })
			)
		    ),
	    	    new Element('h2', { 
	    		html: 'Photo Actions on Thread Pages',
			id: 'FPAOTP.preferences_title',
			styles: {
				display: 'inline',
				'float': 'center',
				'padding': '1px 20px'
			},
			events: {
				'version': function(evt) {
					this.set('html', this.get('html') + ' v' + evt.version);
				}
			}
		    }),
		    new Element('button', {
			html: 'Close',
			'class': 'DeleteButt',
			events: {
				click: function (evt) {
					$('FPAOTP_management_dialog').destroy();
				}
			},
			styles: {
				'float': 'right'
			}
	    	    }),
		    new Element('a', {
			href: "http://www.flickr.com/groups/flickrhacks/discuss/72157626999092722/lastpage",
			html: "feedback thread",
			target: "_blank",
			styles: {
				'float': 'right',
				'font-size': 'smaller',
				'font-style': 'italic',
				padding: '5px 9px'
			}
		    })
	        ),
	        new Element('hr', { styles: { width: '100%' } }),
	        new Element('div', {
	    		align: 'left',
			width: '100%',
			styles: {
				'margin-bottom': '2px'
			}
		}).adopt(
	    		new Element('span', {
	    			html: 'Comments',
				id: 'FPAOTP_management_comments_tab',
				styles: {
					padding: '6px',
					margin: '6px'
				},
				events: {
					'click': function (evt) {
						$('FPAOTP_management_dialog').fireEvent('switch-type', 'comments');
					},
					'switch-type': function (evt) {
						this.setStyles({ 'background-color': evt == 'comments' ? '' : 'lightgrey', color: evt == 'comments' ? '' : 'blue', cursor: evt == 'comments' ? '' : 'pointer' });
					}
				}
			}),
			new Element('span', {
	    			html: 'Tags',
				id: 'FPAOTP_management_tags_tab',
				styles: {
					padding: '6px',
					margin: '6px'
				},
				events: {
					'click': function (evt) {
						$('FPAOTP_management_dialog').fireEvent('switch-type', 'tags');
					},
					'switch-type': function (evt) {
						this.setStyles({ 'background-color': evt == 'tags' ? '' : 'lightgrey', color: evt == 'tags' ? '' : 'blue', cursor: evt == 'tags' ? '' : 'pointer' });
					}
				}
			})
	    	)
	    );
	    getVersion('FPAOTP.preferences_title', 'version');
	    new Element('table', { id: 'FPAOTP_management_table', border: 0, width: '100%' }).adopt(
		    new Element('tr', { valign: 'top' }).adopt(
		    	new Element('td', { align: 'left', styles: { padding: '1px 1px' } }).adopt(
			    new Element('select', {
				id: 'FPAOTP_management_select',
				size: 16,
				multiple: true,
				styles: {
					minWidth: '300px'
				},
				events: {
					change: function (evt) {
						var selection = $('FPAOTP_management_select').getSelected();
						$('FPAOTP_management_dialog').fireEvent('update-selection', selection.length);
			    		},
					'item-added': function (evt) {
						new Element('option', {
							value: evt,
							html: GM_getObject(evt).title
						}).inject(this);
					},
					'item-updated': function (evt) {
						var selection = this.getSelected();
						if (selection.get('value') == evt) { // should always be the case
							selection.set('html', GM_getObject(evt).title);
							this.selectedIndex = -1;
							$('FPAOTP_management_dialog').fireEvent('update-selection', 0);
						}
					},
					'item-removed': function (evt) {
						this.getElements('option').some(function (option) {
							if (option.get('value') == evt) {
								option.dispose();
								return true; // stop as soon as possible
							}
							return false;
						});
						$('FPAOTP_management_dialog').fireEvent('update-selection', 0);
					},
					'switch-type': function (evt) {
						this.empty();
						if (evt == 'comments') {
							storedCommentsCache.forEach( function (key, idx) {
								new Element('option', {
									value: key,
									html: GM_getObject(key).title
								}).inject($('FPAOTP_management_select'));
							});
						} else { // evt == 'tags'
							storedTagsCache.forEach( function (key, idx) {
								new Element('option', {
									value: key,
									html: GM_getObject(key).title
								}).inject($('FPAOTP_management_select'));
							});
						}
					}
				}
			    }),
			    new Element('br'),
			    new Element('img', {
				src: images.editadd,
				id: 'FPAOTP_management_create',
				styles: {
					cursor: 'pointer'
				},
				events: {
					click: function (evt) {
						$('FPAOTP_management_dialog').fireEvent('create-new-item');
					},
					'update-selection': function (evt) {
						// nothing to do
					},
					'switch-type': function (evt) {
						this.set('title', 'create a new stored ' + (evt == 'comments' ? 'comment' : 'tag'));
					}
				}
			    }),
			    new Element('span', { html: ' ' }),
			    new Element('img', {
				id: 'FPAOTP_management_paste',
				src: images.editpaste,
				'class': 'FPAOTPDisabled',
				events: {
					click: function (evt) {
						if (this.hasClass('FPAOTPDisabled')) {
							return;
						}
						var key = $('FPAOTP_management_select').getSelected().get('value');
						$('FPAOTP_management_dialog').fireEvent('item-copy', key);
					},
					'update-selection': function (evt) {
						var selection = parseInt(evt, 10);
						this.set('class', selection == 1 ? 'FPAOTPEnabled' : 'FPAOTPDisabled');
					},
					'switch-type': function (evt) {
						this.set('title', 'create a new stored ' + (evt == 'comments' ? 'comment' : 'tag') + ' based on the selected one');
					}
				}
			    }),
			    new Element('span', { html: ' ' }),
			    new Element('img', {
				id: 'FPAOTP_management_edit',
				src: images.edit,
				'class': 'FPAOTPDisabled',
				events: {
					click: function (evt) {
						if (this.hasClass('FPAOTPDisabled')) {
							return;
						}
						var key = $('FPAOTP_management_select').getSelected().get('value');
						$('FPAOTP_management_dialog').fireEvent('item-edit', key);
					},
					'update-selection': function (evt) {
						var selection = parseInt(evt, 10);
						this.set('class', selection == 1 ?  'FPAOTPEnabled' : 'FPAOTPDisabled');
					},
					'switch-type': function (evt) {
						this.set('title', 'edit the selected stored ' + (evt == 'comments' ? 'comment' : 'tag'));
					}
				}
			    }),
			    new Element('span', { html: ' ' }),
			    new Element('img', {
				id: 'FPAOTP_management_delete',
				src: images.editdelete,
				'class': 'FPAOTPDisabled',
				events: {
					click: function (evt) {
						if (this.hasClass('FPAOTPDisabled')) {
							return;
						}
						$('FPAOTP_management_select').getSelected().forEach( function (selection) {
							GM_deleteValue(selection.get('value'));
							$('FPAOTP_management_dialog').get('FPAOTP-type') == 'comments' ?
								reFillCommentsCache() : reFillTagsCache();
							$('FPAOTP_management_dialog').fireEvent('item-removed', selection.get('value'));
						});
					},
					'update-selection': function (evt) {
						var selection = parseInt(evt, 10);
						this.set('class', selection > 0 ? 'FPAOTPEnabled' : 'FPAOTPDisabled');
					},
					'switch-type': function (evt) {
						this.set('title', 'remove the selected stored ' + evt);
					}
				}
			    }),
			    new Element('br'),
			    new Element('fieldset', { 
				id: 'FPAOTP_referrer_fieldset',
				styles: { 
					'margin-top': '30px', 
					padding: '2px 4px', 
					border: 'black solid 1px' 
				},
				events: {
					'switch-type': function (evt) {
						this.setStyles({ display: evt == 'comments' ? 'block' : 'none', visibility: evt == 'comments' ? 'visible': 'hidden' });
					}
				}
			    }).adopt(
				new Element('legend', { html: 'referrer' }),
				new Element('div', {
					html: 'When invoked on discussion thread pages, you have the option to use a \'referrer\'.<br>' +
						'This referrer can dynamically fill in the thread\'s URL and title, and the link to the ' +
						'group\'s home page, with the group\'s name.'
					}
				),
				new Element('br'),
				new Element('input', {
					type: 'radio',
					id: 'FPAOTP_management_noreferrer',
					name: 'FPAOTP_management_referrer',
					events: {
						click: function (evt) {
							$$('*.FPAOTP_management_referrer').fireEvent('using-no-referrer');
							GM_setValue("add_referrer", "none");
						},
						'switch-type': function (evt) {
							if (evt == 'comments') {
								var addRef = GM_getValue("add_referrer");
								if (!$chk(addRef) || (addRef != 'default' && addRef != 'custom')) {
									this.click(); // fireEvent does not 'select' the radio button
								}
							}
						}
					}
				}),
				new Element('label', {
					'for': 'FPAOTP_management_noreferrer',
					html: 'don\'t use a referrer'
				}),
				new Element('br'),
				new Element('input', {
					type: 'radio',
					id: 'FPAOTP_management_scriptreferrer',
					name: 'FPAOTP_management_referrer',
					events: {
						click: function (evt) {
							$$('*.FPAOTP_management_referrer').fireEvent('using-default-referrer');
							GM_setValue("add_referrer", "default");
						},
						'switch-type': function (evt) {
							if (evt == 'comments') {
								var addRef = GM_getValue("add_referrer");
								if ($chk(addRef) && addRef == "default") {
									this.click(); // fireEvent does not 'select' the radio button
								}
							}
						}
					}
				}),
				new Element('label', {
					'for': 'FPAOTP_management_scriptreferrer',
					html: 'use script\'s default referrer'
				}),
				new Element('br'),
				new Element('input', {
					type: 'radio',
					id: 'FPAOTP_management_customreferrer',
					name: 'FPAOTP_management_referrer',
					events: {
						click: function (evt) {
							$$('*.FPAOTP_management_referrer').fireEvent('using-custom-referrer');
							GM_setValue("add_referrer", "custom");
						},
						'switch-type': function (evt) {
							if (evt == 'comments') {
								var addRef = GM_getValue("add_referrer");
								if ($chk(addRef) && addRef == "custom") {
									this.click(); // fireEvent does not 'select' the radio button
								}
							}
						}
					}
				}),
				new Element('label', {
					'for': 'FPAOTP_management_customreferrer',
					html: 'define your own referrer'
				}),
				new Element('br'),
				new Element('div', {
	    				id: 'FPAOTP_management_referrer_preview',
					'class': 'FPAOTP_management_referrer',
					styles: {
						border: 'solid black 1px',
						'min-height': '60px',
						'min-width': '380px'
					},
					events: {
						'using-no-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
							this.set('html', '');
						},
						'using-default-referrer': function (evt) {
							this.setStyles({ visibility: 'visible', display: 'inline-block' });
							this.set('html', defaultReferrer);
						},
						'using-custom-referrer': function (evt) {
							this.setStyles({ visibility: 'visible', display: 'inline-block' });
							var referrer = GM_getValue("referrer");
							if (!$chk(referrer)) referrer = defaultReferrer;
							this.set('html', referrer.replace(/\n/g, '<br>'));
						},
						'edit-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						},
						'save-referrer': function (evt) {
							this.setStyles({ visibility: 'visible', display: 'inline-block' });
							this.set('html', GM_getValue("referrer").replace(/\n/g, '<br>'));
						}
					}
	    			}),
	    			new Element('textarea', {
					id: 'FPAOTP_management_referrer_edit',
					'class': 'FPAOTP_management_referrer',
					styles: {
						'min-height': '60px',
						'min-width': '380px'
					},
					events: {
						'using-no-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
							this.set('value', '');
						},
						'using-default-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
							this.set('value', defaultReferrer.replace(/<br>/g, '\n'));
						},
						'using-custom-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
							var referrer = GM_getValue("referrer");
							if (!$chk(referrer)) referrer = defaultReferrer;
							this.set('value', referrer.replace(/<br>/g, '\n'));
						},
						'edit-referrer': function (evt) {
							this.setStyles({ visibility: 'visible', display: 'inline-block' });
						},
						'save-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						}
					}
	    			}),
	    			new Element('img', { 
					id: 'FPAOTP_management_referrer_action_edit',
					src: images.edit,
					'class': 'FPAOTP_management_referrer',
					height: '16px',
					events: {
						click: function(evt) {
							$$('*.FPAOTP_management_referrer').fireEvent('edit-referrer');
						},
						'using-no-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						},
						'using-default-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						},
						'using-custom-referrer': function (evt) {
							this.setStyles({ visibility: 'visible', display: 'inline-block' });
						},
						'edit-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						},
						'save-referrer': function (evt) {
							this.setStyles({ visibility: 'visible', display: 'inline-block' });
						}
					},
					styles: {
						cursor: 'pointer'
					}
				}),
	    			new Element('img', { 
					id: 'FPAOTP_management_referrer_action_save',
					src: images.filesave,
					'class': 'FPAOTP_management_referrer',
					height: '16px',
					events: {
						click: function(evt) {
							GM_setValue("referrer", $('FPAOTP_management_referrer_edit').get('value'));
							$$('*.FPAOTP_management_referrer').fireEvent('save-referrer');
						},
						'using-no-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						},
						'using-default-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						},
						'using-custom-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						},
						'edit-referrer': function (evt) {
							this.setStyles({ visibility: 'visible', display: 'inline-block' });
						},
						'save-referrer': function (evt) {
							this.setStyles({ visibility: 'hidden', display: 'none' });
						}
					},
					styles: {
						cursor: 'pointer'
					}
				})
			    ),
			    new Element('fieldset', { 
				id: 'FPAOTP_browserautocomplete_fieldset',
				styles: { 
					'margin-top': '30px', 
					padding: '2px 4px', 
					border: 'black solid 1px' 
				},
				events: {
					'switch-type': function (evt) {
						this.setStyles({ display: evt == 'tags' ? 'block' : 'none', visibility: evt == 'tags' ? 'visible': 'hidden' });
					}
				}
			    }).adopt(
				new Element('legend', { html: 'browser autocomplete' }),
				new Element('div', {
					html: 'On photo pages, Flickr has implemented a feature to suggest tags as you type. '+
						'This option restores the default browser \'autocomplete\' behaviour.<br>' +
						'<i>when you click the \'add a tag\' link, the script waits \'timeout\' milliseconds before doing its magic</i>' +
						' (a good value would be 300ms)'
					}
				),
				new Element('br'),
				new Element('input', {
					type: 'checkbox',
					id: 'FPAOTP_management_tag_autocomplete',
					checked: GM_getValue("restore_autocomplete") == true || GM_getValue("restore_autocomplete") == 'true',
					events: {
						change: function (evt) {
							GM_setValue("restore_autocomplete", this.checked);
						}
					}
				}),
				new Element('label', {
					'for': 'FPAOTP_management_tag_autocomplete',
					html: 'override Flickr\'s tag suggestions'
				}),
				new Element('br'),
				new Element('span', {
					html: 'timeout: '
				}),
				new Element('input', {
					type: 'number',
					value: (function () { 
							var value = GM_getValue("autocomplete_timeout");
							if (!$chk(value)) return 300;
							return value;
						})(),
					events: {
						change: function (evt) {
							GM_setValue("autocomplete_timeout",this.get('value'));
						}
					}
				}),
				new Element('label', { html: 'ms' })
			    )
		    	),
		    	new Element('td', {
				align: 'left',
				styles: { 
					padding: '1px 1px', 
					'background-color': '#DFDFDF'
				}
			}).adopt(
		    	    new Element('label', { html: 'Name: ' }),
		    	    new Element('input', { 
				type: 'text', 
				id: 'FPAOTP_management_name',
				disabled: true,
				events: {
					'update-selection': function (evt) {
						this.set('value', '');
						this.set('disabled', true);
						var selection = parseInt(evt, 10);
						if (selection != 1) {
							return;
						}
						selection = $('FPAOTP_management_select').getSelected();
						this.set('value', GM_getObject(selection.get('value')).title);
					},
					'create-new-item': function (evt) {
						this.set('disabled', false);
						this.set('value', 'Name of new ' + ($('FPAOTP_management_dialog').get('FPAOTP-type') == 'comments' ? 'comment' : 'tag'));
					},
					'item-added': function (evt) {
						this.set('value', '');
						this.set('disabled', true);
					},
					'item-updated': function (evt) {
						this.set('value', '');
						this.set('disabled', true);
					},
					'item-copy': function (evt) {
						this.set('value', GM_getObject(evt).title);
						this.set('disabled', false);
					},
					'item-edit': function (evt) {
						this.set('disabled', false);
					}
				}
			    }),
		    	    new Element('br'),
		    	    new Element('textarea', { 
				id: 'FPAOTP_management_item_edit',
				styles: {
					minWidth: '380px',
					height: '350px',
					visibility: 'hidden',
					display: 'none'
				},
				events: {
					'update-selection': function (evt) {
						this.set('value', '');
						this.setStyle('visibility', 'hidden');
						this.setStyle('display', 'none');
					},
					'create-new-item': function (evt) {
						this.set('value', '');
						this.setStyle('visibility', 'visible');
						this.setStyle('display', 'inline-block');
					},
					'item-added': function (evt) {
						this.set('value', '');
						this.setStyle('visibility', 'invisible');
						this.setStyle('display', 'none');
					},
					'item-updated': function (evt) {
						this.set('value', '');
						this.setStyle('visibility', 'invisible');
						this.setStyle('display', 'none');
					},
					'item-copy': function (evt) {
						this.set('value', GM_getObject(evt).comment);
						this.setStyle('visibility', 'visible');
						this.setStyle('display', 'inline-block');
					},
					'item-edit': function (evt) {
						this.fireEvent('item-copy', evt);
					}
				}
			    }),
			    new Element('div', {
				id: 'FPAOTP_management_item_preview',
				styles: {
					minWidth: '380px',
					height: '350px',
					border: 'solid black 1px'//,
					//display: 'inline-block'
				},
				events: {
					'update-selection': function (evt) {
						this.set('html', '');
						this.setStyle('visibility', 'visible');
						this.setStyle('display', 'inline-block');
						var selection = parseInt(evt, 10);
						if (selection != 1) {
							return;
						}
						selection = $('FPAOTP_management_select').getSelected();
						this.set('html', GM_getObject(selection.get('value')).comment.replace(/\n/g,'<br>'));
					},
					'create-new-item': function (evt) {
						this.empty();
						this.setStyle('visibility', 'hidden');
						this.setStyle('display', 'none');
					},
					'item-added': function (evt) {
						this.empty();
						this.setStyle('visibility', 'visible');
						this.setStyle('display', 'inline-block');
					},
					'item-updated': function (evt) {
						this.empty();
						this.setStyle('visibility', 'visible');
						this.setStyle('display', 'inline-block');
					},
					'item-copy': function (evt) {
						this.setStyle('visibility', 'hidden');
						this.setStyle('display', 'none');
					},
					'item-edit': function (evt) {
						this.fireEvent('item-copy', evt);
					}
				}
			    }),
			    new Element('br'),
			    new Element('div', { // more options
				styles: {
					display: 'inline-block'
				}
			    }).adopt(
			    	new Element('fieldset', { styles: { border: 'black solid 1px', padding: '2px 4px' } }).adopt(
				    new Element('legend').adopt(
				    	new Element('input', {
						type: 'checkbox',
						checked: false,
						id: 'FPAOTP_management_fastcomment',
						disabled: true,
						events: {
							click: function (evt) {
								this.getParent('fieldset').getElements('input').set('disabled', !this.checked);
								this.set('disabled', false);
							},
							'update-selection': function (evt) {
								this.set('checked', false);
								this.set('disabled', true);
								this.getParent('fieldset').getElements('input').set('disabled', true);
								var selection = parseInt(evt, 10);
								if (selection != 1) {
									return;
								}
								selection = $('FPAOTP_management_select').getSelected();
								var storedItem = GM_getObject(selection.get('value'));
								this.set('checked', storedItem.fastcomment == true || storedItem.fastcomment == 'true');
							},
							'create-new-item': function (evt) {
								this.set('disabled', false);
								this.set('checked', false);
							},
							'item-added': function (evt) {
								this.set('checked', false);
								this.getParent('fieldset').getElements('input').set('disabled', true);
							},
							'item-updated': function (evt) {
								this.set('checked', false);
								this.getParent('fieldset').getElements('input').set('disabled', true);
							},
							'item-copy': function (evt) {
								this.set('checked', GM_getObject(evt).fastcomment == true || GM_getObject(evt).fastcomment == 'true');
								this.getParent('fieldset').getElements('input').set('disabled', false);
							},
							'item-edit': function (evt) {
								this.getParent('fieldset').getElements('input').set('disabled', false);
							}
						}
				    	}),
				    	new Element('label', {
						id: 'FPAOTP_management_fastcomment_label',
						'for': 'FPAOTP_management_fastcomment',
						html: 'list as a <i>fast comment</i>',
						events: {
							'switch-type': function (evt) {
								this.set('html', this.get('html').replace(/(comment|tag)/, evt.replace(/s$/, '')));
							}
						}
					})
				    ),
				    new Element('label', {
					'for': 'FPAOTP_management_shortlabel',
					html: 'Short label:'
				    }),
				    new Element('input', {
					id: 'FPAOTP_management_shortlabel', 
					disabled: true,
					events: {
						'update-selection': function (evt) {
							this.set('value', '');
							var selected = parseInt(evt, 10);
							if (selected != 1) {
								this.set('value', '');
								return;
							}
							var selection = $('FPAOTP_management_select').getSelected();
							this.set('value', GM_getObject(selection.get('value')).shortlabel);
						},
						'create-new-item': function (evt) {
							this.set('value', '');
						},
						'item-added': function (evt) {
							this.set('value', '');
						},
						'item-updated': function (evt) {
							this.set('value', '');
						},
						'item-copy': function (evt) {
							this.set('value', GM_getObject(evt).shortlabel);
						}
					}
				    }),
				    new Element('br'),
				    new Element('label', {
					'for': 'FPAOTP_management_bgcolor',
					html: 'Color:'
				    }),
				    new Element('input', {
					id: 'FPAOTP_management_bgcolor',
					disabled: true,
					events: {
						'update-selection': function (evt) {
							this.set('value', '');
							var selected = parseInt(evt, 10);
							if (selected != 1) {
								this.set('value', '');
								return;
							}
							var selection = $('FPAOTP_management_select').getSelected();
							this.set('value', GM_getObject(selection.get('value')).bgcolor);
						},
						'create-new-item': function (evt) {
							this.set('value', '');
						},
						'item-added': function (evt) {
							this.set('value', '');
						},
						'item-updated': function (evt) {
							this.set('value', '');
						},
						'item-copy': function (evt) {
							this.set('value', GM_getObject(evt).bgcolor);
						}
					}
				    }),
				    new Element('br'),
				    new Element('label', {
					'for': 'FPAOTP_management_width',
					html: 'Width:'
				    }),
				    new Element('input', {
					type: 'number',
					min: '16',
					max: '40',
					id: 'FPAOTP_management_width',
					disabled: true,
					events: {
						'change': function (evt) {
							var value = this.get('value');
							if (isNaN(value)) {
								this.set('value', '');
							} else {
								value = parseInt(value, 10);
								if (value < 16) {
									this.set('value', 16);
								} else if (value > 40) {
									this.set('value', 40);
								} else {
									this.set('value', value);
								}
							}
						},
						'update-selection': function (evt) {
							this.set('value', '');
							var selected = parseInt(evt, 10);
							if (selected != 1) {
								this.set('value', '');
								return;
							}
							var selection = $('FPAOTP_management_select').getSelected();
							this.set('value', GM_getObject(selection.get('value')).width);
						},
						'create-new-item': function (evt) {
							this.set('value', '');
						},
						'item-added': function (evt) {
							this.set('value', '');
						},
						'item-updated': function (evt) {
							this.set('value', '');
						},
						'item-copy': function (evt) {
							this.set('value', GM_getObject(evt).width);
						}
					}
				    })
			    	)
			    ),
			    new Element('div', {
				html: ' ',
				styles: {
					display: 'inline-block',
					verticalAlign: 'bottom',
					align: 'right'
				}
			    }).adopt(
			    	new Element('img', {
					id: 'FPAOTP_management_save',
					src: images.filesave,
					'class': 'FPAOTPDisabled',
					alt: 'new-item',
					events: {
						click: function (evt) {
							if (this.hasClass('FPAOTPDisabled')) {
								return;
							}
							this.set('class', 'FPAOTPDisabled'); // prevent double click
							var storagePrefix = 'stored' + $('FPAOTP_management_dialog').get('FPAOTP-type').replace(/s$/, '').replace(/^([a-z])/, function ($1) { return $1.toUpperCase(); });
							var key = this.get('alt') == 'new-item' ? 
									storagePrefix + '.' + new Date().getTime().toString() :
									$('FPAOTP_management_select').getSelected().get('value');
							GM_storeObject(key, {
								title: $('FPAOTP_management_name').get('value'),
								comment: $('FPAOTP_management_item_edit').get('value'),
								fastcomment: $('FPAOTP_management_fastcomment').checked,
								shortlabel: $('FPAOTP_management_shortlabel').get('value'),
								bgcolor: $('FPAOTP_management_bgcolor').get('value'),
								width: $('FPAOTP_management_width').get('value')
							});
							$('FPAOTP_management_dialog').get('FPAOTP-type') == 'comments' ?
								reFillCommentsCache() : reFillTagsCache();
							$('FPAOTP_management_dialog').fireEvent(this.get('alt') == 'new-item' ? 'item-added' : 'item-updated', key);
						},
						'update-selection': function (evt) {
							this.set('class', 'FPAOTPDisabled');
						},
						'create-new-item': function (evt) {
							this.set('class', 'FPAOTPEnabled');
							this.set('alt', 'new-item');
							this.set('title', 'save as new item');
						},
						'item-added': function (evt) {
							this.set('class', 'FPAOTPDisabled');
						},
						'item-updated': function (evt) {
							this.set('class', 'FPAOTPDisabled');
						},
						'item-copy': function (evt) {
							this.set('class', 'FPAOTPEnabled');
							this.set('alt', 'new-item');
							this.set('title', 'save as new item');
						},
						'item-edit': function (evt) {
							this.set('class', 'FPAOTPEnabled');
							this.set('alt', 'edit-item');
							this.set('title', 'save updates');
						}
					}
				})
			    )
		    	)
		    )
	    	
	    ).inject(managementDialog);
	    managementDialog.fireEvent('switch-type', type);
	}
    }
    
    function insertAtCursor(field, value) {
	if (field.selectionStart || field.selectionStart == '0') {
		var startPos = field.selectionStart;
		var endPos = field.selectionEnd;
		var prePart = field.get('value').substring(0, startPos);
		var postPart = field.get('value').substring(endPos, field.get('value').length);
		if (!$chk(postPart)) postPart = '';
		field.set('value', prePart + value + postPart);
	} else {
		field.set('value', field.get('value') + value);
	}
    }

    function toggleCommentDialog(photoId) {
	var commentDialog = $('FPAOTP_commentdialog_' + photoId);
	if ($chk(commentDialog)) {
		commentDialog.destroy();
		return;
	}
	commentDialog = new Element('div', {
            id: 'FPAOTP_commentdialog_' + photoId,
            styles: {
                overflow: 'auto',
                background: '#BFBFBF',
                '-moz-border-radius': '4px',
                '-webkit-border-radius': '4px',
                '-khtml-border-radius': '4px',
                'border-radius': '4px',
                border: 'grey solid 1px',
                padding: '2px 4px',
                display: 'block',
                visibility: 'visible',
		width: '500px',
		'margin-bottom': '5px',
		'text-align': 'left'
            }
        }).inject('FPAOTP_bar_' + photoId, 'after');
	commentDialog.adopt(
	    new Element('label', { html: 'Enter your comment: ' }),
	    new Element('select', { 
	    	id: 'FPAOTP_stored_comments_' + photoId,
		events: {
			change: function (evt) {
			    var combo = $(evt.target);
			    var photoId = combo.get('id').replace('FPAOTP_stored_comments_', '');
			    var selected = combo.getSelected();
			    if (selected.get('value') == -1) {
				$('FPAOTP_commentinput_' + photoId).empty();
			    } else {
				var key = selected.get('value');
				$('FPAOTP_commentinput_' + photoId).set('value', GM_getObject(key).comment);
			    }
			},
			'item-added': function (evt) {
				new Element('option', {
					value: evt,
					html: GM_getObject(evt).title
				}).inject(this);
			},
			'item-updated': function (evt) {
				this.getElements('option').some(function(option) {
					if (option.get('value') == evt) {
						option.set('html', GM_getObject(evt).title);
						return true; // stop as soon as possible
					}
					return false;
				});
			},
			'item-removed': function (evt) {
				this.getElements('option').some(function(option) {
					if (option.get('value') == evt) {
						option.dispose();
						return true; // stop as soon as possible
					}
					return false;
				});
			}
		}
	    }),
	    new Element('img', {
		src: images.db,
		title: 'manage your stored comments',
		styles: {
			cursor: 'pointer'
		},
		events: {
			click: function (evt) {
			    toggleManagementDialog('comments');
			}
		}
	    }),
	    new Element('br'),
	    new Element('textarea', {
		id: 'FPAOTP_commentinput_' + photoId,
		styles: {
			width: '480px',
			height: '100px'
		}
	    })
	);
	if (threadPage) {
	    commentDialog.adopt(
		new Element('br'),
		new Element('input', { 
	 		type: 'checkbox', 
			id: 'FPAOTP_referrer_check_' + photoId,
			events: {
			    click: function(evt) {
				var target = $(evt.target);
				var photoId = target.get('id').replace('FPAOTP_referrer_check_', '');
				if (target.checked) {
					$('FPAOTP_referrer_preview_' + photoId).setStyle('visibility', 'visible');
					$('FPAOTP_referrer_preview_' + photoId).setStyle('display', 'inline-block');
					$('FPAOTP_referrer_action_' + photoId).setStyle('visibility', 'visible');
					$('FPAOTP_referrer_action_' + photoId).setStyle('display', 'inline');
					var referrer = $('FPAOTP_referrer_edit_' + photoId).get('value');
					var threadUrl = document.location.href;
					var threadTitle = searchThreadTitle();
					var groupName = searchGroupName();
					var groupUrl = threadUrl.match(/(.*flickr\.com\/groups\/[^\/]+\/)/)[1];
					referrer = referrer.replace('%__thread_url__%', threadUrl)
							.replace('%__thread_title__%', threadTitle)
							.replace('%__group_url__%', groupUrl)
							.replace('%__group_name__%', groupName)
							.replace(/\n/g, '<br>');
					$('FPAOTP_referrer_preview_' + photoId).set('html', referrer);
				} else {
					$('FPAOTP_referrer_preview_' + photoId).setStyle('visibility', 'hidden');
					$('FPAOTP_referrer_preview_' + photoId).setStyle('display', 'none');
					$('FPAOTP_referrer_action_' + photoId).setStyle('visibility', 'hidden');
					$('FPAOTP_referrer_action_' + photoId).setStyle('display', 'none');
				}
			    }
			}
	    	}),
	    	new Element('label', { 
			html: 'add referrer ', 
			'for': 'FPAOTP_referrer_check_' + photoId, 
			id: 'FPAOTP_referrer_label_' + photoId
	    	}),
	    	new Element('span', {
	    		html: ' (in order to save changes, use the management console)',
			styles: {
				'font-style': 'italic',
				cursor: 'pointer'
			},
			events: {
				click: function (evt) {
					toggleManagementDialog('comments');
				}
			}
	    	}),
	    	new Element('br'),
	    	new Element('div', {
	    		id: 'FPAOTP_referrer_preview_' + photoId,
			styles: {
				border: 'solid black 1px',
				width: '480px',
				visibility: 'hidden',
				display: 'none'
			}
	    	}),
	    	new Element('textarea', {
			id: 'FPAOTP_referrer_edit_' + photoId,
			value: GM_getValue("add_referrer") == "custom" ? GM_getValue("referrer") : defaultReferrer,
			styles: {
				visibility: 'hidden',
				display: 'none',
				width: '480px',
				height: '60px'
			}
	    	}),
	    	new Element('img', { 
			id: 'FPAOTP_referrer_action_' + photoId,
			src: images.edit,
			'class': 'preview_mode',
			height: '16px',
			events: {
				click: function(evt) {
			    		var target = $(evt.target);
			    		var photoId = target.get('id').replace('FPAOTP_referrer_action_', '');
			    		if (target.hasClass('preview_mode')) { // switch to edit mode
						target.set('class', 'edit_mode');
						$('FPAOTP_referrer_check_' + photoId).set('disabled', true);
						$('FPAOTP_referrer_label_' + photoId).setStyle('color', 'grey');
						$('FPAOTP_referrer_preview_' + photoId).setStyle('visibility', 'hidden');
						$('FPAOTP_referrer_preview_' + photoId).setStyle('display', 'none');
						target.set('src', images.filesave);
						$('FPAOTP_commentbutton_' + photoId).set('class', 'DisabledButt');
						var referrer = $('FPAOTP_referrer_edit_' + photoId).get('value');
						if (!$chk(referrer) || referrer.length == 0) {
							referrer  = GM_getValue("referrer");
						}
						if (!$chk(referrer) || referrer.length < 1) {
							referrer = defaultReferrer;
						}
						$('FPAOTP_referrer_edit_' + photoId).setStyle('visibility', 'visible');
						$('FPAOTP_referrer_edit_' + photoId).setStyle('display', 'inline-block');
						$('FPAOTP_referrer_reset_' + photoId).setStyle('visibility', 'visible');
						$('FPAOTP_referrer_reset_' + photoId).setStyle('display', 'inline-block');
						$('FPAOTP_referrer_edit_' + photoId).set('value', referrer.replace(/<br>/g, '\n'));
			    		} else { // switch to preview mode
						target.set('class', 'preview_mode');
						$('FPAOTP_referrer_check_' + photoId).set('disabled', false);
						$('FPAOTP_referrer_label_' + photoId).setStyle('color', '');
						$('FPAOTP_referrer_preview_' + photoId).setStyle('visibility', 'visible');
						$('FPAOTP_referrer_preview_' + photoId).setStyle('display', 'inline-block');
						$('FPAOTP_referrer_edit_' + photoId).setStyle('visibility', 'invisible');
						$('FPAOTP_referrer_edit_' + photoId).setStyle('display', 'none');
						$('FPAOTP_referrer_reset_' + photoId).setStyle('visibility', 'hidden');
						$('FPAOTP_referrer_reset_' + photoId).setStyle('display', 'none');
						target.set('src', images.edit);
						$('FPAOTP_commentbutton_' + photoId).set('class', 'Butt');
						// click the check button twice
						$('FPAOTP_referrer_check_' + photoId).click();
						$('FPAOTP_referrer_check_' + photoId).click();
				        }
				}
			},
			styles: {
				visibility: 'hidden',
				display: 'none',
				cursor: 'pointer'
			}
	    	}),
	    	new Element('div', {
			id: 'FPAOTP_referrer_reset_' + photoId,
			styles: {
				width: '100%',
				'text-align': 'right',
				'font-style': 'italic',
				'cursor': 'pointer',
				'visibility': 'hidden',
				'display': 'none'
			},
			html: 'reset to default referrer',
			events: {
				click: function(evt) {
					var target = $(evt.target);
					var photoId = target.get('id').replace('FPAOTP_referrer_reset_', '');
					$('FPAOTP_referrer_edit_' + photoId).set('value', defaultReferrer.replace(/<br>/g, '\n'));
				}
			}
	    	})
	    );
	}
	commentDialog.adopt( new Element('br'));
	if (photoPage) {
	    commentDialog.adopt(
	    	new Element('br'),
	    	new Element('button', {
		    'class': 'Butt',
		    html: 'USE COMMENT',
		    title: 'copy the comment into Flickr\'s comment box',
		    id: 'FPAOTP_usebutton_' + photoId,
		    styles: {
			margin: '2px'
		    },
		    events: {
			click: function (evt) {
				if (this.hasClass('DisabledButt')) {
					return;
				}
				var photoId = this.get('id').replace('FPAOTP_usebutton_', '');
				var comment = $('FPAOTP_commentinput_' + photoId).get('value');
				$('FPAOTP_commentdialog_' + photoId).destroy();
				if ($chk(comment) && comment.length > 0) {
					insertAtCursor($('comment-form').getElement('textarea'), comment);
				}
			}
		    }
	    	})
	    )
	}
	commentDialog.adopt(
	    new Element('button', {
		'class': 'Butt',
		html: 'POST COMMENT',
		id: 'FPAOTP_commentbutton_' + photoId,
		styles: {
			margin: '2px'
		},
		events: {
			click: function (evt) {
				var target = $(evt.target);
				if (target.hasClass('DisabledButt')) {
					return;
				}
				var photoId = evt.target.id.replace('FPAOTP_commentbutton_', '');
				var comment = $('FPAOTP_commentinput_' + photoId).get('value');
				if (threadPage && $('FPAOTP_referrer_check_' + photoId).checked) {
					var referrer = $('FPAOTP_referrer_preview_' + photoId).get('html');
					comment = comment + '<br>' + referrer;
				}
				$('FPAOTP_commentdialog_' + photoId).destroy();
				if ($chk(comment) && comment.length > 0) {
					var commentIcon = $('FPAOTP_comment_' + photoId);
					commentIcon.empty();
					new Element('img', { src: images.updating, title: 'posting comment' }).inject(commentIcon);
					postComment(photoId, null, comment.replace(/<br[^>]*>/g, '\n'), function (retval) {
						var photoId = retval.photoId;
						var success = retval.success;
						var msg = retval.msg;
						var commentIcon = $('FPAOTP_comment_' + photoId);
						commentIcon.empty();
						new Element('img', {
							src: success ? images.check : images.error,
							title: success ? 'comment posted' : msg
						}).inject(commentIcon);
					});
				}
			}
		}
	    }),
	    new Element('button', {
		'class': 'DisabledButt',
		html: 'Cancel',
		id: 'FPAOTP_cancel_comment_' + photoId,
		styles: {
			margin: '2px'
		},
		events: {
			click: function(evt) {
				var button = $(evt.target);
				var photoId = button.get('id').replace('FPAOTP_cancel_comment_', '');
				toggleCommentDialog(photoId);
				checkIfCommented(photoId);
			}
		},
		styles: {
			cursor: 'pointer'
		}
	    })
	);
	// show referrer preview
	if (threadPage && (GM_getValue("add_referrer") == 'custom' || GM_getValue("add_referrer") == 'default')) {
		$('FPAOTP_referrer_check_' + photoId).click();
	}
	// fill the combo
	new Element('option', {
		value: -1,
		html: '--select a stored comment--'
	}).inject($('FPAOTP_stored_comments_' + photoId));
	storedCommentsCache.forEach( function (key, idx) {
			new Element('option', {
				value: key,
				html: GM_getObject(key).title
			}).inject($('FPAOTP_stored_comments_' + photoId));
		});
	  
	checkIfAllowedToComment(photoId, function (retval) {
		var photoId = retval.photoId;
                var commentIcon = $('FPAOTP_comment_' + photoId);
		commentIcon.empty();
		if (!retval.success) {
			    new Element('img', {
				src: images.error,
				title: retval.msg,
			    }).inject(commentIcon);
		}
	}); // allowed to comment
    }

    function checkIfAllowedToComment(photoId, callback) {
        var commentIcon = $('FPAOTP_comment_' + photoId);
	commentIcon.empty();
	new Element('img', { src: images.updating }).inject(commentIcon);

    	var retval = false;
	var apiData = {
            api_key: GM_getSessionKey(),
            auth_hash: GM_getAuthHash(),
            auth_token: GM_getAuthToken(),
            format: 'json',
            method: 'flickr.photos.getInfo',
	    photo_id: photoId,
            nojsoncallback: 1
        };
	new Request({
		url: 'http://www.flickr.com/',
		onFailure: function (response) {
			callback({ photoId: photoId, success: false, msg: response.statusText });
		},
		onSuccess: function (responseText, responseXml) {
			var result;
                        try {
                            result = JSON.parse(responseText);
                        } catch (e) {
                            result = eval('(' + responseText + ')');
                        }
                        if (result.stat === 'fail') {
				callback({ photoId: photoId, success: false, msg : result.code + " - " + result.message });
                        	return;
                        }
			var cancomment = result.photo.editability.cancomment;
			if (cancomment == 1) {
				callback({ photoId: photoId, success: true });
			} else {
				callback({ photoId: photoId, success: false, msg: "no permission to add comments" });
			}
		}
	}).get("/services/rest/", apiData);
    }

    function postComment(photoId, key, comment, callback) {
	var apiData = {
                    api_key: GM_getSessionKey(),
                    auth_hash: GM_getAuthHash(),
                    auth_token: GM_getAuthToken(),
                    format: 'json',
                    nojsoncallback: 1,
                    method: 'flickr.photos.comments.addComment',
                    photo_id: photoId,
                    comment_text: comment
                };
	new Request({
                    url: "http://www.flickr.com/",
                    onFailure: function (response) {
                        callback({ photoId: photoId, key: key, success: false, msg: response.statusText });
                    },
                    onSuccess: function (responseText, responseXML) {
                        var result;
                        try {
                            result = JSON.parse(responseText);
                        } catch (e) {
                            result = eval('(' + responseText + ')');
                        }  
                        if (result.stat === 'fail') {
                            callback({ photoId: photoId, key: key, success: false, msg: result.code + " - " + result.message });
                        } else {
                            callback({ photoId: photoId, key: key, success: true });
                        }
                    }
                }).get("/services/rest", apiData);
    }

    function showUpdatingIcon(iconId, title, element) {
	new Element('img', {
		src: images.updating,
		id: iconId,
		title: title,
		width: 16,
		styles: {
			position: 'relative',
			top: 6
		}
	}).inject(element);
    }
    function changeImageIntoError(imgElement, error) {
	imgElement.set('src', images.error);
	imgElement.set('title', error);
    }

    function checkIfFaved(photoId, page) {
	// if we already faved this photo, show it
    	if (!$chk(page)) {
		page = 1;
	}
	if (page == 1) {
		showUpdatingIcon('FPAOTP_fav_updating_' + photoId, 'checking fav status for this photo', 'FPAOTP_fav_' + photoId);
	}
	new Request({
		url: 'http://www.flickr.com',
		onFailure: function(response) {
			changeImageIntoError($('FPAOTP_fav_' + photoId).getElement('img'), response.statusText);
		},
		onSuccess: function(responseText, responseXml) {
		    try {
			var result;
			try {
                            result = JSON.parse(responseText);
                        } catch (e) {
                            result = eval('(' + responseText + ')');
                        }
                        //GM_log("result: " + responseText);
                        if (result.stat === 'fail') {
				changeImageIntoError($('FPAOTP_fav_' + photoId).getElement('img'), "ERROR reading favorites: " + result.code + " - " + result.message);
                        	return;
                        }
			var photoSummary = result.photo;
			var myNsid = GM_getUserNsid();
			var faved = photoSummary.person.some(function (person) {
					return person.nsid == myNsid;
				});
			if (faved) {
				var favStar = $('FPAOTP_fav_' + photoId);
				favStar.getElement('img').dispose();
				favStar.set('title', 'Faved');
				favStar.setStyle('background-position', '-1053px -51px');
				favStar.set('class', 'FPATOP_faved');
				return;
			}
			if (photoSummary.pages > page) {
				checkIfFaved(photoId, page+1);
			} else {
				var favStar = $('FPAOTP_fav_' + photoId);
				favStar.getElement('img').dispose(); // show the fav star image
				favStar.set('title', 'Favorite');
				favStar.set('class', 'FPAOTP_unfaved');
			}
		    } catch (e) {
			GM_log("error parsing fav result: " + e);
		    }
		}
	}).get('/services/rest', {
		api_key: GM_getSessionKey(),
                auth_hash: GM_getAuthHash(),
                auth_token: GM_getAuthToken(),
		method: 'flickr.photos.getFavorites',
		photo_id: photoId,
		page: page,
		per_page: 50,
		nojsoncallback: 1,
		format: 'json'
	});
    }

    function checkIfCommented(photoId) {
	// if we already commented on this photo, show it
	showUpdatingIcon('FPAOTP_comment_updating_' + photoId, 'checking comments on this photo', 'FPAOTP_comment_' + photoId);
	new Request({
		url: 'http://www.flickr.com',
		onFailure: function(response) {
			changeImageIntoError($('FPAOTP_comment_' + photoId).getElement('img'), response.statusText);
		},
		onSuccess: function(responseText, responseXml) {
		    try {
			var result;
			try {
                            result = JSON.parse(responseText);
                        } catch (e) {
                            result = eval('(' + responseText + ')');
                        }
                        if (result.stat === 'fail') {
				changeImageIntoError($('FPAOTP_comment_' + photoId).getElement('img'), "ERROR listing photo comments: " + result.code + " - " + result.message);
                        	return;
                        }

			if (!$chk(result.comments) || !$chk(result.comments.comment)) {
				var comments = [];
			} else {
				comments = result.comments.comment;
			}
			var myNsid = GM_getUserNsid();
                        
			var commented = $chk(comments) && comments.some(function (comment) {
				return comment.author == myNsid;
			});
			var commentIcon = $('FPAOTP_comment_' + photoId);
			commentIcon.getElement('img').dispose(); // spinning balls
			if (commented) {
				commentIcon.set('title', 'Already commented');
				commentIcon.adopt(new Element('img', { src: images.check_big }));
				commentIcon.set('class', 'FPATOP_commented');
				return;
			} else {
				commentIcon.set('class', 'FPAOTP_uncommented');
			}
		    } catch (e) {
			GM_log("error parsing comment result: " + e);
		    }
		}
	}).get('/services/rest', {
		api_key: GM_getSessionKey(),
                auth_hash: GM_getAuthHash(),
                auth_token: GM_getAuthToken(),
		method: 'flickr.photos.comments.getList',
		photo_id: photoId,
		nojsoncallback: 1,
		format: 'json'
	});
    }

    function favorite(photoId) {
	showUpdatingIcon('FPAOTP_faving_' + photoId, 'fav\'ing this photo', 'FPAOTP_fav_' + photoId);
	new Request({
		url: 'http://www.flickr.com',
		onFailure: function(response) {
			changeImageIntoError($('FPAOTP_faving_' + photoId), response.statusText);
		},
		onSuccess: function(responseText, responseXml) {
		    try {
			var result;
			try {
                            result = JSON.parse(responseText);
                        } catch (e) {
                            result = eval('(' + responseText + ')');
                        }
                        //GM_log("result: " + responseText);
                        if (result.stat === 'fail') {
				changeImageIntoError($('FPAOTP_faving_' + photoId), "ERROR adding favorite: " + result.code + " - " + result.message);
                        	return;
                        } else {
				$('FPAOTP_faving_' + photoId).dispose();
				var favStar = $('FPAOTP_fav_' + photoId);
				favStar.set('title', 'Faved');
				favStar.setStyle('background-position', '-1053px -51px');
				favStar.set('class', 'FPAOTP_faved');
				return;
			}
		    } catch (e) {
			GM_log("error parsing faving result: " + e);
		    }
		}
	}).get('/services/rest', {
		api_key: GM_getSessionKey(),
                auth_hash: GM_getAuthHash(),
                auth_token: GM_getAuthToken(),
		method: 'flickr.favorites.add',
		photo_id: photoId,
		nojsoncallback: 1,
		format: 'json'
	});
    }

    function unFavorite(photoId) {
	showUpdatingIcon('FPAOTP_unfaving_' + photoId, 'unfav\'ing this photo', 'FPAOTP_fav_' + photoId);
	new Request({
		url: 'http://www.flickr.com',
		onFailure: function(response) {
			changeImageIntoError($('FPAOTP_unfaving_' + photoId), response.statusText);
		},
		onSuccess: function(responseText, responseXml) {
		    try {
			var result;
			try {
                            result = JSON.parse(responseText);
                        } catch (e) {
                            result = eval('(' + responseText + ')');
                        }
                        //GM_log("result: " + responseText);
                        if (result.stat === 'fail') {
				changeImageIntoError($('FPAOTP_unfaving_' + photoId), "ERROR removing favorite: " + result.code + " - " + result.message);
                        	return;
                        } else {
				$('FPAOTP_unfaving_' + photoId).dispose();
				var favStar = $('FPAOTP_fav_' + photoId);
				favStar.set('title', 'Favorite');
				favStar.setStyle('background-position', '-1053px -11px');
				favStar.set('class', 'FPAOTP_unfaved');
				return;
			}
		    } catch (e) {
			GM_log("error parsing unfaving result: " + e);
		    }
		}
	}).get('/services/rest', {
		api_key: GM_getSessionKey(),
                auth_hash: GM_getAuthHash(),
                auth_token: GM_getAuthToken(),
		method: 'flickr.favorites.remove',
		photo_id: photoId,
		nojsoncallback: 1,
		format: 'json'
	});
    }

// update code

function showUpdateNotification(data) {
    var onlineVersion = data.onlineVersion;
    var version = data.version;
    var beta = data.beta;

    var color = 'white';
    var updatespan = $('AlesaDams_updates_span');
    if (!$chk(updatespan)) {
    	updatespan = new Element('span', {
		id: 'AlesaDams_updates_span',
		// copied from Google++ userscript:
		styles: {
		    padding: '2px 4px',
		    display: 'block',
		    '-moz-background-clip': 'border',
		    '-moz-background-origin': 'padding',
		    '-moz-background-inline-policy': 'continuous',
		    position: 'fixed',
		    opacity: '0.7',
		    'z-index': 1011,
		    bottom: '5px',
		    right: '5px',
		    left: '5px'
		}
	    }).inject($(document).getElement("body"));
    }
    var bgColor = beta ? 'grey' : 'black';
    var updater = new Element('span', {
    	styles: {
		background: bgColor + ' none repeat scroll 0% 0%',
		margin: '2px 5px',
		position: 'relative',
		'float': beta ? 'left' : 'right',
		opacity: beta ? '0.5' : ''
	}
    }).inject(updatespan);
    new Element('a', {
        html: 'Flickr Photo Actions on Thread Pages: ' + (beta ? 'beta ' + version : onlineVersion + ' available'),
        href: 'https://chrome.google.com/webstore/detail/aockpladnfogjkcohbmciemdcjdhcbkn',
        target: '_blank',
        title:  'to the ' + (beta ? 'stable version' : '') + ' install page (opens in new tab)',
        styles: {
            'color': color,
            'text-decoration': 'none'
        }
    }).inject(updater);
    new Element('a', {
        html: beta ? ' (feedback)' : ' (Changes)',
        title: 'opens in new tab',
        href: 'http://www.flickr.com/groups/flickrhacks/discuss/72157626999092722/lastpage',
        styles: {
            'text-decoration': 'none'
        },
        target: '_blank'
    }).inject(updater);
}

function outOfDate(version, onlineVersion) {
        var reVersionMatch      = /(\d+)\.(\d+)/;
        var onlineVersionParts  = reVersionMatch.exec(onlineVersion);
        var currentVersionParts = reVersionMatch.exec(version);
        var onlineVersionMajor, onlineVersionMinor;
        //[ onlineVersion, onlineVersionMajor, onlineVersionMinor, onlineVersionBuild ] = onlineVersionParts; 'invalid left-hand side' in Chrome
        onlineVersionMajor = onlineVersionParts[1];
        onlineVersionMinor = onlineVersionParts[2];
        var currentVersionMajor, currentVersionMinor;
        //[ currentVersion, currentVersionMajor, currentVersionMinor, currentVersionBuild] = currentVersionParts;
        currentVersionMajor = currentVersionParts[1];
        currentVersionMinor = currentVersionParts[2];
        // first check major: important update! => rewrite, flickr updates, greasemonkey updates
        if (parseInt(onlineVersionMajor, 10) > parseInt(currentVersionMajor, 10)) {
            return true;
        } else if (parseInt(onlineVersionMajor, 10) === parseInt(currentVersionMajor, 10)) { // we don't want to downgrade  
            // minor version update => new functionality
            if (parseInt(onlineVersionMinor, 10) > parseInt(currentVersionMinor, 10)) {
                return true;
            }
        }
        return false;
}

function getThreadInfo(data) {
        var threadId = data.threadId;
        var callback = data.callback;

        try {  
                new Request({
                        url: 'http://www.flickr.com',
                        onSuccess: function (responseText, responseXML) {
                                    try {
                                        var result;
                                        try {
                                            result = JSON.parse(responseText);
                                        } catch (e) {
                                                GM_log("error parsing JSON reply for thread " + threadId + " (" + e + "): '" + responseText);
                                                callback({ threadId: threadId, success: false, message: e });
                                                return;
                                        }
                                        if (result.stat === 'fail') {
                                                callback({ threadId: threadId, success: false, message: result.message });
                                        } else {
                                                callback({ threadId: threadId, success: true, message: result.topic.message._content });
                                        }
                                    } catch (e) {
                                        GM_log("ERROR processing result: " + e);
                                        callback({ threadId: threadId, success: false, message: "ERROR processing result: " + e });
                                    }
                                },
                                onFailure: function (response) {
                                        GM_log("error: " + response.statusText);
                                        callback({ threadId: threadId, success: false, message: response.statusText });
                                }
                }).get('/services/rest', {
            		api_key: GM_getSessionKey(),
                        auth_hash: GM_getAuthHash(),
                        auth_token: GM_getAuthToken(),
                        format: 'json',
                        method: 'flickr.groups.discuss.topics.getInfo',
                        nojsoncallback: 1,
                        topic_id: threadId
                });
        } catch (e) {
                callback({ threadId: threadId, success: false, message: 'Exception: ' + e });
        }
}

function checkVersion(version) {
    var lastVersionCheckTime = GM_getValue("lastVersionCheckTime");
    var elapsedtime;
    var CPStartTime = new Date();
    if ($chk(lastVersionCheckTime)) {
        elapsedtime = CPStartTime.getTime() - lastVersionCheckTime;
    }
    if (!$chk(lastVersionCheckTime) || elapsedtime / 1000 > 60 * 60 * 12) { //more then 12h ago
	getThreadInfo({ threadId: '72157626999092722', callback: function(retval) {
                        var success = retval.success;
                        var message = retval.message;
                        if (!success) {
                                GM_log("error getting version info: " + message);
                                GM_deleteValue("onlineVersion");
                                GM_deleteValue("lastVersionCheckTime");
                                return;
                        }
                        var onlineVersion = message.split(/<i>current stable version:\s*/)[1].split(/<\/i>/)[0];
                        GM_setValue("onlineVersion", onlineVersion);
                        GM_setValue("lastVersionCheckTime", (new Date()).getTime().toString());
                }
        });
        GM_setValue("lastVersionCheckTime", CPStartTime.getTime().toString());
    }

    var onlineVersion = GM_getValue("onlineVersion");
    if ($chk(onlineVersion)) {
        var updateAvailable = outOfDate(version, onlineVersion);
        if (updateAvailable) {
            showUpdateNotification({ onlineVersion: onlineVersion });
        } else if (version != onlineVersion) { // not out of date, but still different => beta
	    showUpdateNotification({ version: version, beta: true });
	}
    }
}


	new Element('span', {
		id: 'FPAOTP-updatenotifier',
		styles: {
			display: 'none',
			visibility: 'hidden'
		},
		events: {
			'checkversion': function (evt) {
				checkVersion(evt.version);
			}
		}
	}).inject($$('body')[0], 'bottom');
	getVersion('FPAOTP-updatenotifier', 'checkversion');

// end update code

    if (photoPage) {
	var restore_autocomplete = GM_getValue("restore_autocomplete");
	if (restore_autocomplete == true || restore_autocomplete == 'true') {
		try {
			$$('a.tagadderlink').each(function(anchor) { 
				anchor.addEventListener('click', function(evt) {
					setTimeout(function() {
					// Chrome needs a different implementation
						var form = document.getElementById('tagadderform');
						var input = document.getElementById('addtagbox');
						var new_form = form.cloneNode(true); // remove any event listeners
						form.setAttribute('id', 'oldtagadderform');
						input.setAttribute('id', 'oldaddtagbox');
						var new_input = $(new_form).getElement('input[id=addtagbox]');
						new_input.setAttribute('autocomplete', 'on');
						new_form.setAttribute('autocomplete', 'on');
						form.parentNode.replaceChild(new_form,form);

						new Element('input', { type: 'submit', value: 'add tag', 'class': 'Butt' }).inject($(new_input).getParent('form'));
						$(input).dispose();
						new_input.focus();
					}, (function() { 
						var timeout = GM_getValue("autocomplete_timeout"); 
						if (!$chk(timeout)) return 300;
						try {
							timeout = parseInt(timeout,10);
						} catch (e) {
							return 300;
						}
						if (isNaN(timeout)) return 300;
						return timeout;
					   })());
				}, true);
			});
		} catch (e) {
			GM_log("ERROR: unable to add event listener: " + e);
		}
	}
    }

